package com.ccarlos.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccarlos.enums.BizCodeEnum;
import com.ccarlos.enums.CouponCategoryEnum;
import com.ccarlos.enums.CouponPublishEnum;
import com.ccarlos.enums.CouponStateEnum;
import com.ccarlos.exception.BizException;
import com.ccarlos.interceptor.LoginInterceptor;
import com.ccarlos.mapper.CouponRecordMapper;
import com.ccarlos.model.CouponDO;
import com.ccarlos.mapper.CouponMapper;
import com.ccarlos.model.CouponRecordDO;
import com.ccarlos.model.LoginUser;
import com.ccarlos.request.NewUserCouponRequest;
import com.ccarlos.service.CouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ccarlos.util.CommonUtil;
import com.ccarlos.util.JsonData;
import com.ccarlos.vo.CouponVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ccarlos
 * @since 2022-01-13
 */
@Slf4j
@Service
public class CouponServiceImpl implements CouponService {

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private CouponRecordMapper couponRecordMapper;


    @Override
    public Map<String, Object> pageCouponActivity(int page, int size) {
        //第1页，每页2条
        Page<CouponDO> pageInfo = new Page<>(page, size);
        IPage<CouponDO> couponDOPage = couponMapper.selectPage(pageInfo, new QueryWrapper<CouponDO>()
                .eq("publish", CouponPublishEnum.PUBLISH)
                .eq("category", CouponCategoryEnum.PROMOTION)
                .orderByDesc("create_time"));

        Map<String, Object> pageMap = new HashMap<>(3);

        pageMap.put("total_record", couponDOPage.getTotal());
        pageMap.put("total_page", couponDOPage.getPages());
        pageMap.put("current_data", couponDOPage.getRecords().stream().map(obj -> beanProcess(obj)).collect(Collectors.toList()));

        return pageMap;
    }

    private CouponVO beanProcess(CouponDO couponDO) {
        CouponVO couponVO = new CouponVO();
        BeanUtils.copyProperties(couponDO, couponVO);
        return couponVO;
    }

    /**
     * 领劵接口
     * 1、获取优惠券是否存在
     * 2、校验优惠券是否可以领取：时间、库存、超过限制
     * 3、扣减库存
     * 4、保存领劵记录
     * <p>
     * 始终要记得，羊毛党思维很厉害，社会工程学 应用的很厉害
     *
     * @param couponId
     * @param category
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public JsonData addCoupon(long couponId, CouponCategoryEnum category) {

        LoginUser loginUser = LoginInterceptor.threadLocal.get();

//        String lockKey = "lock:coupon:"+couponId +":"+loginUser.getId();
//        RLock rLock = redissonClient.getLock(lockKey);
//
//        //多个线程进入，会阻塞等待释放锁，默认30秒，然后有watch dog自动续期
//        rLock.lock();

        //加锁10秒钟过期，没有watch dog功能，无法自动续期
        //rLock.lock(10,TimeUnit.SECONDS);

        log.info("领劵接口加锁成功:{}", Thread.currentThread().getId());
        //try{

        CouponDO couponDO = couponMapper.selectOne(new QueryWrapper<CouponDO>()
                .eq("id", couponId)
                .eq("category", category.name()));

        //优惠券是否可以领取
        this.checkCoupon(couponDO, loginUser.getId());

        //构建领劵记录
        CouponRecordDO couponRecordDO = new CouponRecordDO();
        BeanUtils.copyProperties(couponDO, couponRecordDO);
        couponRecordDO.setCreateTime(new Date());
        couponRecordDO.setUseState(CouponStateEnum.NEW.name());
        couponRecordDO.setUserId(loginUser.getId());
        couponRecordDO.setUserName(loginUser.getName());
        couponRecordDO.setCouponId(couponId);
        couponRecordDO.setId(null);

        //扣减库存
        int rows = couponMapper.reduceStock(couponId);

        //int flag = 1/0;

        if (rows == 1) {
            //库存扣减成功才保存记录
            couponRecordMapper.insert(couponRecordDO);

        } else {
            log.warn("发放优惠券失败:{},用户:{}", couponDO, loginUser);

            throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }

//            } finally {
//                rLock.unlock();
//                log.info("解锁成功");
//            }


        return JsonData.buildSuccess();

    }

    /**
     * 校验是否可以领取
     *
     * @param couponDO
     * @param userId
     */
    private void checkCoupon(CouponDO couponDO, Long userId) {

        if (couponDO == null) {
            throw new BizException(BizCodeEnum.COUPON_NO_EXITS);
        }

        //库存是否足够
        if (couponDO.getStock() <= 0) {
            throw new BizException(BizCodeEnum.COUPON_NO_STOCK);
        }

        //判断是否是否发布状态
        if (!couponDO.getPublish().equals(CouponPublishEnum.PUBLISH.name())) {
            throw new BizException(BizCodeEnum.COUPON_GET_FAIL);
        }

        //是否在领取时间范围
        long time = CommonUtil.getCurrentTimestamp();
        long start = couponDO.getStartTime().getTime();
        long end = couponDO.getEndTime().getTime();
        if (time < start || time > end) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_TIME);
        }

        //用户是否超过限制
        int recordNum = couponRecordMapper.selectCount(new QueryWrapper<CouponRecordDO>()
                .eq("coupon_id", couponDO.getId())
                .eq("user_id", userId));

        if (recordNum >= couponDO.getUserLimit()) {
            throw new BizException(BizCodeEnum.COUPON_OUT_OF_LIMIT);
        }


    }

    /**
     * 用户微服务调用的时候，没传递token
     * <p>
     * 本地直接调用发放优惠券的方法，需要构造一个登录用户存储在threadlocal
     *
     * @param newUserCouponRequest
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public JsonData initNewUserCoupon(NewUserCouponRequest newUserCouponRequest) {
        LoginUser loginUser = new LoginUser();
        loginUser.setId(newUserCouponRequest.getUserId());
        loginUser.setName(newUserCouponRequest.getName());
        LoginInterceptor.threadLocal.set(loginUser);

        //查询新用户有哪些优惠券
        List<CouponDO> couponDOList = couponMapper.selectList(new QueryWrapper<CouponDO>()
                .eq("category", CouponCategoryEnum.NEW_USER.name()));

        for (CouponDO couponDO : couponDOList) {
            //幂等操作，调用需要加锁
            this.addCoupon(couponDO.getId(), CouponCategoryEnum.NEW_USER);

        }

        //int b = 1/0;
        return JsonData.buildSuccess();
    }
}
